﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Volo.Abp.Application.Services;
using Volo.Abp.Domain.Repositories;
using Volo.Abp;
using Volo.Abp.Auditing;
using Volo.Abp.Domain.Entities;
using EcoSystem.Enums;
using Microsoft.EntityFrameworkCore;
using Newtonsoft.Json;

namespace EcoSystem
{
    /// <summary>
    /// 配置信息
    /// </summary>
    public class ConfigSettingAppService : CrudAppService<
         ConfigSetting,
         ConfigSettingDto,
         Guid,
         GetConfigSettingInput,
         CreateOrUpdateConfigSettingDto,
         CreateOrUpdateConfigSettingDto>, IConfigSettingAppService
    {
        private readonly IRepository<ConfigDefine, Guid> _defineRepository;
        private readonly IRepository<ConfigCategory, string> _categoryRepository;

        private readonly IAuditingManager _auditingManager;
        public ConfigSettingAppService(
           IAuditingManager auditingManager,
            IRepository<ConfigSetting, Guid> repository,
            IRepository<ConfigDefine, Guid> configDefineRepository,
            IRepository<ConfigCategory, string> categoryRepository)
            : base(repository)
        {
            _defineRepository = configDefineRepository;
            _auditingManager = auditingManager;
            _categoryRepository = categoryRepository;
        }

        /// <summary>
        /// 获取指定配置类别对应的配置定义及配置值
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        /// <exception cref="UserFriendlyException"></exception>
        public async Task<ConfigDefineAndSettingDto> GetConfigSettingAsync(GetConfigDefineAndSettingInputDto input)
        {
            var defineItems = await (await _defineRepository.GetQueryableAsync()).Where(x => x.Category == input.Category).ToListAsync();
            var category = (await _categoryRepository.GetQueryableAsync()).FirstOrDefault(x => x.Id == input.Category);
            if (category == null)
            {
                throw new UserFriendlyException("参数错误，配置分类不存在");
            }
            var result = new ConfigDefineAndSettingDto()
            {
                CategoryId = category.Id,
                CategoryName = category.Name,
                DefineFields = ObjectMapper.Map<List<ConfigDefine>, List<ConfigDefineDto>>(defineItems),
                Values = new List<ConfigSettingValues>()
            };

            var query = (await Repository.GetQueryableAsync()).Where(x => x.Category == input.Category)
                .WhereIf(!string.IsNullOrEmpty(input.ModuleCode), x => x.ModuleCode == input.ModuleCode)
                .WhereIf(input.Level.HasValue && input.Level.Value.HasFlag(ConfigLevel.G), x => x.Level == ConfigLevel.G)
                .WhereIf(input.Level.HasValue && input.Level.Value.HasFlag(ConfigLevel.S), x => x.Level == ConfigLevel.S || x.Level == ConfigLevel.G)
                .Where(x => x.Level != ConfigLevel.U || x.ProviderKey == CurrentUser.Id.ToString());

            var items = await query.ToListAsync();
            var moduleCodes = new List<string>();
            if (!string.IsNullOrEmpty(input.ModuleCode))
            {
                moduleCodes.Add(input.ModuleCode);
            }
            else
            {
                moduleCodes = category.ModuleScope;
            }
            //生成配置值数据

            if (!input.Level.HasValue || input.Level.Value.HasFlag(ConfigLevel.G))
            {
                //全局配置数据
                var values = new ConfigSettingValues()
                {
                    Level = ConfigLevel.G,
                    ModuleCode = "",
                    Values = CreateModelValues(defineItems, items, ConfigLevel.G)
                };
                result.Values.Add(values);
            }
            foreach (var moduleCode in moduleCodes)
            {
                //各子系统配置数据（系统级+用户级)
                if (!input.Level.HasValue || input.Level.Value.HasFlag(ConfigLevel.S))
                {
                    var values = new ConfigSettingValues()
                    {
                        Level = ConfigLevel.S,
                        ModuleCode = moduleCode,
                        Values = CreateModelValues(defineItems, items, ConfigLevel.S, moduleCode)
                    };
                    result.Values.Add(values);
                }

                if (!input.Level.HasValue || input.Level.Value.HasFlag(ConfigLevel.U))
                {
                    var values = new ConfigSettingValues()
                    {
                        Level = ConfigLevel.U,
                        ModuleCode = moduleCode,
                        Values = CreateModelValues(defineItems, items, ConfigLevel.U, moduleCode)
                    };
                    result.Values.Add(values);
                }
            }
            return result;
        }

        /// <summary>
        /// 获取指定类别的用户配置值
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public async Task<ConfigDefineAndSettingDto> GetUserConfigSettingAsync(GetConfigDefineAndSettingInputDto input)
        {
            input.Level = ConfigLevel.U;
            return await GetConfigSettingAsync(input);
        }

        /// <summary>
        /// 获取指定模块的配置值
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public async Task<Dictionary<string, Dictionary<string, object>>> GetConfigAsync(GetSettingsInput input)
        {
            if (string.IsNullOrEmpty(input.ModuleCode) && input.Level != ConfigLevel.G)
            {
                return new Dictionary<string, Dictionary<string, object>>();
            }

            var queryble = (await _categoryRepository.GetQueryableAsync()).AsQueryable<ConfigCategory>();
            //获取全部分类
            var cateogryItems = await (from a in (await _categoryRepository.GetQueryableAsync()) select a)
                .WhereIf(!string.IsNullOrEmpty(input.Category), x => x.Id == input.Category)
                .WhereIf(input.Usage.HasValue, x => x.Usage == input.Usage).ToListAsync();
            if (input.Level != ConfigLevel.G)
            {
                cateogryItems = cateogryItems.Where(x =>
                    x.Level.HasFlag(ConfigLevel.G) || x.ModuleScope.Any(m => m.Equals(input.ModuleCode))).ToList();
            }
            //获取分类定义值
            var defineItems = await (from a in (await _defineRepository.GetQueryableAsync()) select a)
                    .WhereIf(!string.IsNullOrEmpty(input.Category), x => x.Category == input.Category).ToListAsync();

            //查询配置数据
            var query = from a in (await Repository.GetQueryableAsync())
                        select a;
            query = query.WhereIf(!string.IsNullOrEmpty(input.Category), x => x.Category.Equals(input.Category))
                .Where(x => x.Usage != Usage.Back);

            switch (input.Level)
            {
                case ConfigLevel.S:
                    query = query.Where(x => x.Level == ConfigLevel.G ||
                    (x.Level == input.Level && x.ModuleCode.Equals(input.ModuleCode)));
                    break;
                case ConfigLevel.U:
                    query = query.Where(x => x.Level == input.Level &&
                    x.ProviderKey.Equals(CurrentUser.Id.ToString()) &&
                    x.ModuleCode.Equals(input.ModuleCode));
                    break;
                case ConfigLevel.G:
                default:
                    query = query.Where(x => x.Level == ConfigLevel.G);
                    break;
            }
            var list = await query.ToListAsync();

            var result = new Dictionary<string, Dictionary<string, object>>();
            foreach (var category in cateogryItems)
            {
                var items = defineItems.Where(x => x.Category == category.Id);
                var configValues = new Dictionary<string, object>();
                foreach (var defineItem in items)
                {
                    //取值项在配置中未定义则不处理
                    if (configValues.ContainsKey(defineItem.Key))
                    {
                        continue;
                    }
                    ConfigSetting item = GetConfigItem(input.ModuleCode, defineItem.Key, category.Id, input.Level, list);
                    if (item != null)
                    {
                        configValues.Add(item.Key, JsonConvert.DeserializeObject(item.Value));
                    }
                    else if (!string.IsNullOrEmpty(defineItem.DefaultValue))
                    {
                        var defaultValue = JsonConvert.DeserializeObject(defineItem.DefaultValue);
                        configValues.Add(defineItem.Key, defaultValue);
                    }
                    else
                    {
                        configValues.Add(defineItem.Key, GetDefaultValue(defineItem));
                    }
                }
                result.Add(category.Id, configValues);
            }
            return result;
        }

        private ConfigSetting GetConfigItem(string moduleCode, string key, string categoryId, ConfigLevel currentLevel, List<ConfigSetting> allItems)
        {
            ConfigSetting item = null;
            if (currentLevel == ConfigLevel.G)
            {
                //全局配置
                item = allItems.FirstOrDefault(x =>
                    x.Key == key &&
                    x.Category == categoryId &&
                    x.Level == ConfigLevel.G);
            }
            else
            {
                //当前级别配置
                item = allItems.FirstOrDefault(x =>
                    x.Key == key &&
                    x.ModuleCode == moduleCode &&
                    x.Category == categoryId &&
                    x.Level == currentLevel);
                if (currentLevel == ConfigLevel.U)
                {
                    //如果当前级别为用户级则取值为空则默认取系统级配置
                    if (IsNullItemValue(item))
                    {
                        item = allItems.FirstOrDefault(x =>
                            x.Key == key &&
                            x.Category == categoryId &&
                            x.ModuleCode == moduleCode &&
                            x.Level == ConfigLevel.S
                            );
                    }
                }
                //当用户级和系统级取值为空则默认取全局配置
                if (IsNullItemValue(item))
                {
                    item = allItems.FirstOrDefault(x =>
                        x.Key == key &&
                        x.Category == categoryId &&
                        x.Level == ConfigLevel.G);
                }
            }
            return item;
        }

        /// <summary>
        /// 判断配置项值是否为空
        /// </summary>
        /// <param name="item"></param>
        /// <returns></returns>
        private bool IsNullItemValue(ConfigSetting item)
        {
            return item == null || item.Value == "\"\"" || item.Value == "" || item.Value == "null";
        }

        /// <summary>
        /// 保存指定分类对应的配置数据
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        /// <exception cref="UserFriendlyException"></exception>
        public async Task<bool> SaveConfigAsync(SaveSettingsInput input)
        {
            if (string.IsNullOrEmpty(input.Category))
            {
                throw new UserFriendlyException("参数错误，配置类别编码不允许为空");
            }
            var category = await _categoryRepository.FirstOrDefaultAsync(x => x.Id == input.Category);
            if (category == null)
            {
                throw new UserFriendlyException("参数错误，配置类别信息不存在");

            }

            var defineItems = await (await _defineRepository.GetQueryableAsync()).Where(x =>
            x.Category == input.Category).Select(x => x.Key).ToListAsync();

            var allConfigs = await (await Repository.GetQueryableAsync()).Where(x =>
                x.Level == input.Level && x.Category == input.Category &&
                (input.Level == ConfigLevel.G || x.ModuleCode == input.ModuleCode)).ToListAsync();

            foreach (var item in input.Configs)
            {
                if (!defineItems.Any(x => x == item.Key))
                {
                    //配置定义不存在则跳过
                    if (allConfigs.Any(x => x.Key == item.Key))
                    {
                        await Repository.DeleteAsync(x => x.Key == item.Key);
                    }
                    continue;
                }
                var entity = allConfigs.FirstOrDefault(x => x.Key == item.Key);
                if (entity != null)
                {
                    //更新配置
                    entity.Value = JsonConvert.SerializeObject(item.Value);
                    await Repository.UpdateAsync(entity);
                }
                else
                {
                    //插入配置
                    entity = new ConfigSetting()
                    {
                        Key = item.Key,
                        Value = JsonConvert.SerializeObject(item.Value),
                        Category = input.Category,
                        Level = input.Level,
                        ModuleCode = input.Level == ConfigLevel.G ? ":" : input.ModuleCode,
                    };
                    EntityHelper.TrySetId(entity, () => GuidGenerator.Create());
                    if (entity.Level == ConfigLevel.U)
                    {
                        entity.ProviderKey = CurrentUser.ToString();
                    }
                    await Repository.InsertAsync(entity);
                }
            }
            return true;
        }


        /// <summary>
        /// 保存指定分类对应的配置数据
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        /// <exception cref="UserFriendlyException"></exception>
        public async Task<bool> SaveAllConfigAsync(SaveAllSettingsInput input)
        {
            foreach (var category in input.Configs.Keys)
            {
                var configs = input.Configs[category];
                await SaveConfigAsync(new SaveSettingsInput()
                {
                    Category = category,
                    Level = input.Level,
                    ModuleCode = input.ModuleCode,
                    Configs = configs
                });
            }
            return true;
        }

        /// <summary>
        /// 保存指定分类对应的用户配置数据
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public async Task<bool> SaveUserConfigAsync(SaveUserSettingsInput input)
        {
            return await SaveConfigAsync(new SaveSettingsInput()
            {
                Category = input.Category,
                ModuleCode = input.ModuleCode,
                Level = ConfigLevel.U,
                Configs = input.Configs
            });
        }

        /// <summary>
        /// 批量删除梗塞 
        /// </summary>
        /// <param name="category"></param>
        /// <returns></returns>
        public async Task DeleteAllAsync(string category)
        {
            await Repository.DeleteAsync(x => x.Category == category);
        }

        public async Task DeleteForKeyAsync(string key)
        {
            await Repository.DeleteAsync(x => x.Key == key);
        }

        /// <summary>
        /// 获取配置定义对应的默认配置值 
        /// </summary>
        /// <param name="define"></param>
        /// <returns></returns>
        private object GetDefaultValue(ConfigDefine define)
        {
            if (!string.IsNullOrEmpty(define.DefaultValue))
            {
                return JsonConvert.DeserializeObject(define.DefaultValue);
            }
            else
            {
                if (define.KeyType == "string")
                {
                    return "";
                }
                else if (define.KeyType == "bool")
                {
                    return false;
                }
                else if (define.KeyType == "number")
                {
                    return 0;
                }
                else if (define.KeyType == "date")
                {
                    return "";
                }
                else if (define.KeyType == "json")
                {
                    return new { };
                }
                else if (define.KeyType == "array")
                {
                    return new object[] { };
                }
                return null;
            }
        }

        /// <summary>
        /// 根据配置定义和配置列表数据生成配置模型数据（键值对）
        /// </summary>
        /// <param name="defines"></param>
        /// <param name="list"></param>
        /// <param name="level"></param>
        /// <param name="moduleCode"></param>
        /// <returns></returns>
        private Dictionary<string, object> CreateModelValues(List<ConfigDefine> defines, List<ConfigSetting> list, ConfigLevel level, string moduleCode = null)
        {
            var dict = new Dictionary<string, object>();
            foreach (var defineItem in defines)
            {
                var item = list.FirstOrDefault(x => x.Key == defineItem.Key && x.Level == level && (string.IsNullOrEmpty(moduleCode) || x.ModuleCode == moduleCode));
                if (item == null && level == ConfigLevel.U)
                {
                    item = list.FirstOrDefault(x => x.Key == defineItem.Key &&
                    x.ModuleCode == moduleCode && x.Level == ConfigLevel.S);
                }
                if (item == null && level != ConfigLevel.G)
                {
                    item = list.FirstOrDefault(x => x.Key == defineItem.Key && x.Level == ConfigLevel.G);
                }
                if (item != null)
                {
                    dict.Add(item.Key, JsonConvert.DeserializeObject(item.Value));
                }
                else if (!string.IsNullOrEmpty(defineItem.DefaultValue))
                {
                    var defaultValue = JsonConvert.DeserializeObject(defineItem.DefaultValue);
                    dict.Add(defineItem.Key, defaultValue);
                }
                else
                {
                    dict.Add(defineItem.Key, GetDefaultValue(defineItem));
                }
            }
            return dict;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        /// <exception cref="NotImplementedException"></exception>
        public async Task<string> GetConfigValueAsync(GetConfigValueInputDto input)
        {
            var hasCategory = await _categoryRepository.AnyAsync(x =>
                x.Id == input.Category && x.ConfigType == ConfigType.Interface);
            if (hasCategory)
            {
                var items = await (await Repository.GetQueryableAsync())
                    .WhereIf(!string.IsNullOrWhiteSpace(input.ModuleCode), x => x.ModuleCode == input.ModuleCode)
                    .Where(x =>
                    x.Category == input.Category &&
                    x.Key == input.Key &&
                    x.Level == ConfigLevel.U &&
                    x.ProviderKey == CurrentUser.Id.ToString()).ToListAsync();
                if (items.Count > 1)
                {
                    return "[" + string.Join(",", items.Select(x => x.Value).ToList()) + "]";
                }
                else if (items.Count == 1)
                {
                    return items[0].Value;
                }
                return null;
            }
            else
            {
                throw new UserFriendlyException("参数错误，配置分类未定义或对应分类不允许接口访问");
            }
        }

        public async Task<bool> SetConfigValueAsync(SetConfigValueInputDto input)
        {
            var hasCategory = await _categoryRepository.AnyAsync(x =>
                x.Id == input.Category && x.ConfigType == ConfigType.Interface);
            if (hasCategory)
            {
                var config = await Repository.FirstOrDefaultAsync(x =>
                  x.Category == input.Category &&
                  x.Key == input.Key &&
                  x.ModuleCode == input.ModuleCode &&
                  x.Level == ConfigLevel.U &&
                  x.ProviderKey == CurrentUser.Id.ToString());
                if (config != null)
                {
                    config.Value = input.Value;
                    await Repository.UpdateAsync(config, true);
                    return true;
                }
                else
                {
                    config = new ConfigSetting()
                    {
                        Category = input.Category,
                        ModuleCode = input.ModuleCode,
                        Key = input.Key,
                        Level = ConfigLevel.U,
                        ProviderKey = CurrentUser.Id.ToString(),
                        Value = input.Value,
                    };
                    EntityHelper.TrySetId(config, () => GuidGenerator.Create());
                    await Repository.InsertAsync(config, true);
                    return true;
                }
            }
            else
            {
                throw new UserFriendlyException("参数错误，配置分类未定义或对应分类不允许接口访问");
            }
        }
    }
}
